/*
 * @Author: zhangxinyue
 * @Date: 2020-10-26
 * @Description: 流程设计
 */
import React, { Component } from 'react';
import { notification } from 'antd';
import PropTypes from 'prop-types';
import { connect } from 'dva';

// Bpmn 相关文件
import EditingTools from './BpmnEditor/EditingTools';
import BpmnModeler from './BpmnEditor/Modeler';
import styles from './BpmnEditor/sources/Bpmn.less';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';

// 右侧属性相关文件
import SequenceFlowPannel from '../sequenceFlowPannel';
import NoSelectNode from '../noSelectNode';
import UserTaskPannel from '../userTaskPannel';
import {
  getSequenceFlowChild,
  setDefualtFlow,
  getNodeAttr,
  getTaskType,
  getStation,
  setNodeAttr,
  taskType,
  taskTypeObj,
  delNodeAttr,
  addChildNode,
  delChildNode,
  delNode,
  setSequenceFlowChild,
  findUserTaskType,
  setSequenceFlow,
} from '../commonFunc';
import OtherNodePanel from '../otherNodePanel';
import { diagramXML } from './emptyXml';

class ProcessDesign extends Component {
  // props默认值
  static defaultProps = {
    diagramXML: undefined, // 默认无流程图数据
    attributeData: undefined, // 默认无属性信息
    height: '100%', // 默认高度占父容器100%
    width: '100%', // 默认宽度占父容器100%
    onRef: () => {}, // 本组件的引用
    onClick: () => {},
  };

  state = {
    scale: 1, // 流程图比例
    type: undefined, // 点击节点类型
    id: undefined, // 节点ID
    name: undefined, // 节点名称
    condition: undefined, // 节点条件（通过 or 驳回，仅在type为SequenceFlow时生效）
    taskType: undefined, // 审批类型
    station: undefined, // 岗位
    user: undefined, // 用户
  };

  sequenceId = 'bpmn:SequenceFlow'; // xml中网关条件的key值

  userTask = 'bpmn:UserTask'; // xml中usertask key值

  componentDidMount() {
    // 父组件对本组件的引用
    this.props.onRef(this);
    this.queryStationUserList(); // 查询岗位和用户列表
    // 初始化BpmnModeler对象
    this.bpmnModeler = new BpmnModeler({
      container: '#canvas',
      // 设置属性面板
      propertiesPanel: {
        parent: '#properties-panel',
      },
    });

    // 渲染流程图
    this.renderDiagram(this.props.diagramXML === undefined ? diagramXML : this.props.diagramXML, 'first');

    // 删除 bpmn logo
    const bjsIoLogo = document.querySelector('.bjs-powered-by');
    while (bjsIoLogo.firstChild) {
      bjsIoLogo.removeChild(bjsIoLogo.firstChild);
    }

    // 初始化xml对象
    const parser = new DOMParser();
    this.xmlDoc = parser.parseFromString(this.props.diagramXML === undefined ? diagramXML : this.props.diagramXML, 'text/xml');

    // 添加节点点击事件
    const eventBus = this.bpmnModeler.get('eventBus');
    eventBus.on('element.changed', (e) => {
      const tempNode = e.element.businessObject;
      if (this.state.id == tempNode.id && this.state.name != tempNode.name) {
        // 节点id相同 且名称不同
        this.setState({
          name: tempNode.name, // 实时更新节点名称
        });
      }
    });
    eventBus.on('element.click', (e) => {
      const node = e.element.businessObject;

      // 点击的节点是流程
      if (node.$type == this.sequenceId && node.sourceRef.$type === 'bpmn:ExclusiveGateway') {
        this.setState({
          type: this.sequenceId, // 节点类型
          id: node.id, // 节点ID
          name: node.name, // 节点名称
          condition: getSequenceFlowChild(this.xmlDoc, node.id), // 流程条件
          expression: node.conditionExpression,
        });
      } else if (node.$type == this.userTask) {
        // 点击的节点是usertask
        const taskType = getTaskType(this.xmlDoc, node.id); // 任务类型
        this.setState({
          type: this.userTask, // 节点类型
          id: node.id, // 节点ID
          name: node.name, // 节点名称
          taskType, // 任务类型
          station: getStation(this.xmlDoc, node.id, taskType), // 岗位
          user:
            getNodeAttr(this.xmlDoc, node.id, 'activiti:assignee') == '${per}' || getNodeAttr(this.xmlDoc, node.id, 'activiti:assignee') == '${starter}'
              ? null
              : getNodeAttr(this.xmlDoc, node.id, 'activiti:assignee'), // 用户
        });
      } else if (node.$type !== 'bpmn:Process') {
        this.setState({
          type: 'other',
          id: node.id,
          name: node.name || '',
        });
      }

      // 抛出节点点击事件
      this.props.onClick(e.element);
    });
  }

  // 查询岗位和用户列表
  queryStationUserList = () => {
    this.props.dispatch({
      type: 'bpmnM/getStationList',
      payload: {},
      callback: (data) => {
        this.setState({
          stationList: [...data],
        });
      },
    });
    this.props.dispatch({
      type: 'bpmnM/getUserList',
      payload: {},
      callback: (data) => {
        this.setState({
          userList: [...data],
        });
      },
    });
  };

  /**
   * 下载xml/svg
   *  @param  type  类型  svg / xml
   *  @param  data  数据
   *  @param  name  文件名称
   */
  download = (type, data, name) => {
    let dataTrack = '';
    const a = document.createElement('a');

    switch (type) {
      case 'xml':
        dataTrack = 'bpmn';
        break;
      case 'svg':
        dataTrack = 'svg';
        break;
      default:
        break;
    }

    name = name || `diagram.${dataTrack}`;

    // encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。
    a.setAttribute('href', `data:application/bpmn20-xml;charset=UTF-8,${encodeURIComponent(data)}`);
    a.setAttribute('target', '_blank');
    a.setAttribute('dataTrack', `diagram:download-${dataTrack}`);
    a.setAttribute('download', name);

    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  };

  // “导入 xml 文件”按钮
  handleOpenFile = (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    let data = '';
    reader.readAsText(file);
    reader.onload = (event) => {
      data = event.target.result;
      // 渲染流程图
      this.renderDiagram(data, 'first');
      const parser = new DOMParser();
      this.xmlDoc = parser.parseFromString(data, 'text/xml');
    };
  };

  // “撤销”按钮
  handleUndo = () => {
    this.bpmnModeler.get('commandStack').undo();
  };

  // “恢复”按钮
  handleRedo = () => {
    this.bpmnModeler.get('commandStack').redo();
  };

  // “重置”、“放大”、“缩小”按钮
  handleZoom = (radio) => {
    const newScale = !radio ? 1.0 : this.state.scale + radio <= 0.2 ? 0.2 : this.state.scale + radio;

    this.bpmnModeler.get('canvas').zoom(newScale);
    this.setState({
      scale: newScale,
    });
  };

  // “下载文件”按钮（.bpmn文件）
  handleDownloadXml = () => {
    this.bpmnModeler.saveXML({ format: true }, (err, data) => {
      this.download('xml', data);
    });
  };

  // “下载图片”按钮（.svg格式图片）
  handleDownloadSvg = () => {
    this.bpmnModeler.saveSVG({ format: true }, (err, data) => {
      this.download('svg', data);
    });
  };

  // 保存
  handleSave = (callback) => {
    this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
      // 将xml字符串转换成xml doc对象
      const parser = new DOMParser();
      const all = parser.parseFromString(xml, 'text/xml');
      // 删除incoming和outgoing节点
      delNode(all, 'bpmn2:incoming');
      delNode(all, 'bpmn2:outgoing');
      if (this.props.isOrder) {
        // 回购指令 交易所质押式回购指令
        setDefualtFlow(all);
      }

      // 添加域名控件（后台解析需要），前端会在转xml时丢失
      let str = new XMLSerializer().serializeToString(all);
      str = str.replace(/\${approvalResult == "Y"}/g, '<![CDATA[${approvalResult == "Y"}]]>');
      str = str.replace(/\${passCount==nrOfInstances}/g, '<![CDATA[${passCount==nrOfInstances}]]>');
      str = str.replace(/\${approvalResult == "N"}/g, '<![CDATA[${approvalResult == "N"}]]>');
      str = str.replace(/\${rejectCount &gt; 0}/g, '<![CDATA[${rejectCount > 0}]]>');
      callback(str);
    });
  };

  // 公共方法 - 渲染流程图
  renderDiagram = (xml, type) => {
    this.bpmnModeler.importXML(xml, (err) => {
      if (err) {
        notification.error({
          message: '提示',
          description: '导入失败',
        });
      } else if (type) {
        // 加载xml文件时 自适应画布，其他情况不进入
        const canvas = this.bpmnModeler.get('canvas');
        canvas.zoom('fit-viewport', 'auto'); // 流程图自适应画布
      }
    });
  };

  // 折叠
  handlePanelFold = () => {
    const { hidePanel } = this.state;
    this.setState(
      {
        hidePanel: !hidePanel,
        hideCount: 1,
      },
      () => {},
    );
  };

  // 将设置的条件保存至XML
  saveFlow = (e) => {
    this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
      const parser = new DOMParser();
      this.xmlDoc = parser.parseFromString(xml, 'text/xml');
      // 判断该条件的前一个网关的前一个userTask节点是否是多人并发审批或多人顺序审批
      const nodeType = findUserTaskType(this.xmlDoc, e.id);
      let tag = false;
      // 若是多人并发或多人顺序，应在保存条件时做特殊处理
      if (nodeType === taskTypeObj.concurrent || nodeType === taskTypeObj.order) {
        tag = true;
      }
      // 保存条件名称
      setNodeAttr(this.xmlDoc, e.id, 'name', e.name);
      // 保存条件
      setSequenceFlowChild(this.xmlDoc, e.id, e.condition, tag, e.conditionExpression);
      // 更新state里的条件和名称
      this.setState({
        name: e.name,
        condition: e.condition,
      });
      // 重新渲染流程图
      this.renderDiagram(new XMLSerializer().serializeToString(this.xmlDoc));
    });
  };

  // 保存userTask
  saveUserTask = (e) => {
    this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
      const parser = new DOMParser();
      this.xmlDoc = parser.parseFromString(xml, 'text/xml');
      // 保存名称
      setNodeAttr(this.xmlDoc, e.id, 'name', e.name);
      // 更新state里的名称
      this.setState({
        name: e.name,
      });
      // 保存类型
      // eslint-disable-next-line default-case
      switch (e.taskType) {
        // 单人审批
        case taskTypeObj.single:
          delNodeAttr(this.xmlDoc, e.id, taskType[1].candidateGroups); // 删除“多人抢占”模式下设置的属性
          setNodeAttr(this.xmlDoc, e.id, taskType[0].assignee, e.user); // 设置“单人审批”模式下的属性
          delChildNode(this.xmlDoc, e.id); // 删除“多人并发”和“多人顺序”时设置的子节点
          setSequenceFlow(this.xmlDoc, e.id, taskTypeObj.single); // 设置该节点后一个网关的“出线”条件
          // 更新state里的user
          this.setState({
            user: e.user,
          });
          break;
        // 多人抢占审批
        case taskTypeObj.seize:
          // delNodeAttr(this.xmlDoc, e.id, taskType[0].assignee);   // 删除“单人审批”、“多人并发”或“多人顺序”模式下设置的属性
          // setNodeAttr(this.xmlDoc, e.id, taskType[1].candidateGroups, e.station); // 设置“多人抢占”模式下的属性
          // delChildNode(this.xmlDoc, e.id);    // 删除“多人并发”和“多人顺序”时设置的子节点
          // //更新state里的station
          // this.setState({
          //     station:  e.station,
          // })
          setNodeAttr(this.xmlDoc, e.id, taskType[1].assignee, taskType[1].assigneeValue); // 设置“多人抢占”模式下的属性
          addChildNode(this.xmlDoc, e.id, e.station, 'false', true); // 添加多人抢占模式下，当前节点的子节点
          setSequenceFlow(this.xmlDoc, e.id, taskTypeObj.seize); // 设置该节点后一个网关的“出线”条件
          // 更新state里的station
          this.setState({
            station: e.station,
          });
          break;
        // 多人并发审批
        case taskTypeObj.concurrent:
          // delNodeAttr(this.xmlDoc, e.id, taskType[1].candidateGroups);   // 删除“多人抢占”模式下设置的属性
          setNodeAttr(this.xmlDoc, e.id, taskType[2].assignee, taskType[2].assigneeValue); // 设置“多人并发”模式下的属性
          addChildNode(this.xmlDoc, e.id, e.station, 'false', false); // 添加多人并发模式下，当前节点的子节点
          setSequenceFlow(this.xmlDoc, e.id, taskTypeObj.concurrent); // 设置该节点后一个网关的“出线”条件
          // 更新state里的station
          this.setState({
            station: e.station,
          });
          break;
        // 多人顺序审批
        case taskTypeObj.order:
          // delNodeAttr(this.xmlDoc, e.id, taskType[1].candidateGroups);   // 删除“多人抢占”模式下设置的属性
          setNodeAttr(this.xmlDoc, e.id, taskType[3].assignee, taskType[3].assigneeValue); // 设置“多人顺序”模式下的属性
          addChildNode(this.xmlDoc, e.id, e.station, 'true', false); // 添加多人并发模式下，当前节点的子节点
          setSequenceFlow(this.xmlDoc, e.id, taskTypeObj.order); // 设置该节点后一个网关的“出线”条件
          // 更新state里的station
          this.setState({
            station: e.station,
          });
          break;
        // 发起人
        case taskTypeObj.sponsor:
          delNodeAttr(this.xmlDoc, e.id, taskType[1].candidateGroups); // 删除“多人抢占”模式下设置的属性
          setNodeAttr(this.xmlDoc, e.id, taskType[4].assignee, taskType[4].assigneeValue); // 设置“发起人”模式下的属性
          delChildNode(this.xmlDoc, e.id); // 删除“多人并发”和“多人顺序”时设置的子节点
          break;
      }
      // 重新渲染流程图
      const newXml = new XMLSerializer().serializeToString(this.xmlDoc);
      this.renderDiagram(newXml);
    });
  };

  // 将其他条件设置保存至XML
  saveOtherNode = (e) => {
    this.bpmnModeler.saveXML({ format: true }, (err, xml) => {
      const parser = new DOMParser();
      this.xmlDoc = parser.parseFromString(xml, 'text/xml');
      // 保存条件名称
      setNodeAttr(this.xmlDoc, e.id, 'name', e.name);
      // 更新state里的名称
      this.setState({
        name: e.name,
      });
      // 重新渲染流程图
      this.renderDiagram(new XMLSerializer().serializeToString(this.xmlDoc));
    });
  };

  taskTypeChanged = (e) => {
    this.setState({
      taskType: e,
    });
  };

  render() {
    const { type } = this.state;
    const height = this.props.height || '1000px';
    return (
      <div style={{ height }}>
        <div className={styles.container} id='js-drop-zone'>
          <div className={styles.bpmnTilte}>流程图</div>
          {/* 流程图画板 */}
          <div className={styles.canvas} id='canvas' style={{ height: `calc(${height} - 31px)` }} />
          {/* 右上角工具栏定义 */}
          <EditingTools
            onOpenFIle={this.handleOpenFile} // 打开文件
            onUndo={this.handleUndo} // 撤销
            onRedo={this.handleRedo} // 恢复
            onZoomReset={() => this.handleZoom()} // 重置
            onZoomIn={() => this.handleZoom(0.1)} // 放大
            onZoomOut={() => this.handleZoom(-0.1)} // 缩小
            onDownloadXml={this.handleDownloadXml} // 下载bpmn格式文件
            onDownloadSvg={this.handleDownloadSvg} // 下载SVG格式图片
          />
        </div>
        {type === undefined && <NoSelectNode />}
        {type === this.sequenceId && ( // 条件节点
          <SequenceFlowPannel
            id={this.state.id}
            name={this.state.name}
            condition={this.state.condition}
            xmlDoc={this.xmlDoc}
            mode='editor'
            isOrder={this.props.isOrder} // 指令情况下
            saveFlow={this.saveFlow}
            expression={this.state.expression}
            flowType={this.props.flowType}
            flowChildType={this.props.flowChildType}
          />
        )}
        {type === this.userTask && (
          <UserTaskPannel
            id={this.state.id}
            name={this.state.name}
            taskType={this.state.taskType}
            station={this.state.station}
            user={this.state.user}
            mode='editor'
            xmlDoc={this.xmlDoc}
            saveUserTask={this.saveUserTask}
            taskTypeChanged={this.taskTypeChanged}
            stationList={this.state.stationList}
            userList={this.state.userList}
          />
        )}
        {type === 'other' && <OtherNodePanel id={this.state.id} name={this.state.name} mode='editor' saveOtherNode={this.saveOtherNode} />}
      </div>
    );
  }
}

// props类型
ProcessDesign.propsTypes = {
  diagramXML: PropTypes.string, // 流程图数据
  onClick: PropTypes.func, // 按钮点击事件
  height: PropTypes.string, // 高度
  width: PropTypes.string, // 宽度
  onRef: PropTypes.func, // 本组件的引用（可用来父组件调用内部方法）
  isOrder: PropTypes.bool, // 当前模式是指令
};

export default connect(({ bpmnM }) => ({ bpmnM }))(ProcessDesign);
